#include "Common.h"

#include <termios.h>

#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

#include "App_Files.h"
#include "BspUart.h"

typedef struct _UART_BAUD_RATE_MAP_s_
{
    UINT32 BitRate;
    UINT32 DevRate;
} UART_BAUD_RATE_MAP_s;

static UART_BAUD_RATE_MAP_s gstUartSpeedMaps[] = 
{
    {UART_BAUD_RATE_B300,    B300},
    {UART_BAUD_RATE_B1200,   B1200},
    {UART_BAUD_RATE_B2400,   B2400},
    {UART_BAUD_RATE_B4800,   B4800},
    {UART_BAUD_RATE_B9600,   B9600},
    {UART_BAUD_RATE_B19200,  B19200},
    {UART_BAUD_RATE_B38400,  B38400},
    {UART_BAUD_RATE_B57600,  B57600},
    {UART_BAUD_RATE_B115200, B115200},
};

static USHORT gusUartSpeedCnt = ARRAY_SIZE(gstUartSpeedMaps);

BSP_UART_CFG_s gstUartCfg[BSP_UART_CHANNEL_MAX] = 
{
    {
        .isEnable    = BOOL_FALSE,
        .iFd         = -1,
        .szPath      = BSP_UART_CHANNEL0_NAME,
        .enBaudRate  = UART_BAUD_RATE_B115200,
        .enDataBits  = UART_DATA_BITS_8,
        .enParityChk = UART_PARITY_CHK_NONE,
        .enStopBits  = UART_STOP_BITS_1,
        .enFlowCtrl  = UART_FLOW_CTRL_NONE,
    },
};

/**********************************************************
 * Function Name:        BspUart_SetSpeed
 * Description:          设置串口波特率
 * Parameters[in]:       IN SINT32 baud_rate
 * Parameters[out]:
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetSpeed(IN UART_BAUD_RATE_e enBaudRate, INOUT struct termios *pstTerm)
{
    SINT32 i = 0;

    for (i = 0; i < gusUartSpeedCnt; i++)
    {
        if (gstUartSpeedMaps[i].BitRate == enBaudRate)
        {
            break;
        }
    }

    if (i >= gusUartSpeedCnt)
    {
        LOG_ERROR("Unsupport Speed %d", enBaudRate);
        return STAT_ERR_UART_CFG;
    }

    cfsetispeed(pstTerm, gstUartSpeedMaps[i].DevRate);
    cfsetospeed(pstTerm, gstUartSpeedMaps[i].DevRate);

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        BspUart_SetFlowCtrl
 * Description:          设置串口流控
 * Parameters[in]:       IN SINT32 flow_ctrl
 *                       INOUT struct termios *pstTerm
 * Parameters[out]:      INOUT struct termios *pstTerm
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetFlowCtrl(IN UART_FLOW_CTRL_e enFlowCtrl, INOUT struct termios *pstTerm)
{
    switch(enFlowCtrl)
    {
        case UART_FLOW_CTRL_NONE :
        {
            pstTerm->c_cflag &= ~CRTSCTS;
            pstTerm->c_cflag &= ~IXON;
            pstTerm->c_cflag &= ~IXOFF;
            break;
        }

        case UART_FLOW_CTRL_RTS_CTS :
        {
            pstTerm->c_cflag |= CRTSCTS;
            pstTerm->c_cflag &= ~IXON;
            pstTerm->c_cflag &= ~IXOFF;
            break;
        }

        case UART_FLOW_CTRL_XON_XOFF :
        {
            pstTerm->c_cflag |= IXON | IXOFF;
            pstTerm->c_cflag &= ~CRTSCTS;
            break;
        }

        default:
        {
            LOG_ERROR("Unsupported flow ctrl %d", enFlowCtrl);
            return STAT_ERR_INVAILD_PARAM;
            break;
        }
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        BspUart_SetDataBits
 * Description:          设置串口数据位
 * Parameters[in]:       IN SINT32 data_bits
 *                       INOUT struct termios *pstTerm
 * Parameters[out]:      INOUT struct termios *pstTerm
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetDataBits(IN UART_DATA_BITS_e enDataBits, INOUT struct termios *pstTerm)
{
    pstTerm->c_cflag &= ~CSIZE;

    switch (enDataBits)
    {
        case UART_DATA_BITS_5:
            pstTerm->c_cflag |= CS5;
            break;
        case UART_DATA_BITS_6:
            pstTerm->c_cflag |= CS6;
            break;
        case UART_DATA_BITS_7:
            pstTerm->c_cflag |= CS7;
            break;
        case UART_DATA_BITS_8:
            pstTerm->c_cflag |= CS8;
            break;
        default:
            LOG_ERROR("Unsupported data bits %d", enDataBits);
            return STAT_ERR_INVAILD_PARAM;
            break;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        BspUart_SetParityChk
 * Description:          设置串口校验
 * Parameters[in]:       IN CHAR parity
 *                       INOUT struct termios *pstTerm
 * Parameters[out]:      INOUT struct termios *pstTerm
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetParityChk(IN UART_PARITY_CHK_e enParityChk, INOUT struct termios *pstTerm)
{
    switch (enParityChk)
    {
        case UART_PARITY_CHK_NONE://无校验
        {
            pstTerm->c_cflag &= ~PARENB;
            pstTerm->c_iflag &= ~INPCK;
            break;
        }

        case UART_PARITY_CHK_ODD://奇校验
        {
            pstTerm->c_cflag |= PARENB;
            pstTerm->c_cflag |= PARODD;
            pstTerm->c_iflag |= (INPCK | ISTRIP);
            break;
        }

        case UART_PARITY_CHK_EVEN://偶校验
        {
            pstTerm->c_cflag |= PARENB;
            pstTerm->c_cflag &= ~PARODD;
            pstTerm->c_iflag |= (INPCK | ISTRIP);
            break;
        }

        case UART_PARITY_CHK_SPACE:
        {
            pstTerm->c_cflag &= ~PARENB;
            pstTerm->c_cflag &= ~CSTOPB;
            break;
        }

        case UART_PARITY_CHK_MARK:
        {
            pstTerm->c_cflag &= ~PARENB;
            pstTerm->c_cflag |= CSTOPB;
            break;
        }

        default:
        {
            LOG_ERROR("Unsupported parity %d", enParityChk);
            return STAT_ERR_INVAILD_PARAM;
            break;
        }
    }
    
    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        AppLibUart_SetStopBits
 * Description:          设置串口停止位
 * Parameters[in]:       IN UART_STOP_BITS_e enStopBits
 *                       INOUT struct termios *pstTerm
 * Parameters[out]:      INOUT struct termios *pstTerm
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetStopBits(IN UART_STOP_BITS_e enStopBits, INOUT struct termios *pstTerm)
{
    switch (enStopBits)
    {
        case UART_STOP_BITS_1:
        {
            pstTerm->c_cflag &= ~CSTOPB;
            break;
        }

        case UART_STOP_BITS_2:
        {
            pstTerm->c_cflag |= CSTOPB;
            break;
        }

        default:
        {
            LOG_ERROR("Unsupported stop bits %d", enStopBits);
            return STAT_ERR_INVAILD_PARAM;
            break;
        }
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        BspUart_SetCfg
 * Description:          设置串口配置
 * Parameters[in]:       IN LIB_UART_INFO_s *pstUart
 * Parameters[out]:      
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_SetCfg(IN BSP_UART_CFG_s *pstUart)
{
    ULONG ulRet = STAT_SUCCESS;
    struct termios options;

    if(tcgetattr(pstUart->iFd, &options) != 0)
    {
        LOG_ERROR("com %s get error: %s!", pstUart->szPath, strerror(errno));
        return STAT_ERR_FILE_TCGET;
    }

    ulRet = BspUart_SetSpeed(pstUart->enBaudRate, &options);
    if (ulRet != STAT_SUCCESS)
    {
        return ulRet;
    }

    ulRet = BspUart_SetFlowCtrl(pstUart->enFlowCtrl, &options);
    if (ulRet != STAT_SUCCESS)
    {
        return ulRet;
    }

    ulRet = BspUart_SetDataBits(pstUart->enDataBits, &options);
    if (ulRet != STAT_SUCCESS)
    {
        return ulRet;
    }

    ulRet = BspUart_SetParityChk(pstUart->enParityChk, &options);
    if (ulRet != STAT_SUCCESS)
    {
        return ulRet;
    }

    ulRet = BspUart_SetStopBits(pstUart->enStopBits, &options);
    if (ulRet != STAT_SUCCESS)
    {
        return ulRet;
    }

    options.c_cc[VTIME] = 0; //设置超时
    options.c_cc[VMIN] = 0; //设置最小字节数

    options.c_cflag |=  (CLOCAL | CREAD); // 启用接收器，忽略调制解调器状态

    options.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG | ECHONL); // 禁用特殊处理字符

    // 原始输出模式
    options.c_oflag &= ~OPOST;
    options.c_oflag &= ~ONLCR;

    tcflush(pstUart->iFd,TCIFLUSH);

    if (tcsetattr(pstUart->iFd,TCSANOW,&options) != 0)
    {
        LOG_ERROR("com %s set error: %s!", pstUart->szPath, strerror(errno));
        return STAT_ERR_FILE_TCSET;
    }

    return STAT_SUCCESS;
}

/**********************************************************
 * Function Name:        BspUart_Open
 * Description:          打开串口
 * Parameters[in]:       INOUT IN BSP_UART_CFG_s *pstUart
 * Parameters[out]:      INOUT IN BSP_UART_CFG_s *pstUart
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_Open(IN BSP_UART_CHANNEL_e enChan, INOUT SINT32 *piFd)
{
    ULONG ulRet = STAT_SUCCESS;
    BSP_UART_CFG_s *pstUart = NULL;

    if (enChan >= BSP_UART_CHANNEL_MAX || piFd == NULL)
    {
        LOG_ERROR("Invalid channel %d or fd %p", enChan, piFd);
        return STAT_ERR_INVAILD_PARAM;
    }

    pstUart = &gstUartCfg[enChan];
    if (pstUart->isEnable == BOOL_FALSE)
    {
        LOG_ERROR("Chan %d is disable", enChan);
        return STAT_ERR_UART_DISABLE;
    }

    pstUart->iFd = open(pstUart->szPath, O_RDWR | O_NOCTTY | O_NDELAY);
    if (pstUart->iFd < 0)
    {
        LOG_ERROR("Open %s fail, err %s", pstUart->szPath, strerror(errno));
        return STAT_ERR_FILE_OPEN;
    }

    if(isatty(pstUart->iFd) == 0)
    {
        LOG_ERROR("%s is not a terminal device", pstUart->szPath);
        close(pstUart->iFd);
        pstUart->iFd = -1;
        return STAT_ERR_UART_TYPE;
    }

    if(fcntl(pstUart->iFd, F_SETFL, 0) < 0)
    {
        LOG_ERROR("fcntl %s fail, err %s", pstUart->szPath, strerror(errno));
        close(pstUart->iFd);
        pstUart->iFd = -1;
        return STAT_ERR_FILE_FCNTL;
    }

    ulRet = BspUart_SetCfg(pstUart);
    if (ulRet != STAT_SUCCESS)
    {
        close(pstUart->iFd);
        pstUart->iFd = -1;
        return ulRet;
    }

    *piFd = pstUart->iFd;

    return ulRet;
}

ULONG BspUart_Read(IN BSP_UART_CHANNEL_e enChan, INOUT UINT8 *pucBuf, IN SINT32 iLen)
{
    ULONG ulRet = STAT_SUCCESS;

    if (enChan >= BSP_UART_CHANNEL_MAX || pucBuf == NULL || iLen <= 0)
    {
        LOG_ERROR("Invalid channel %d or buf %p or len %d", enChan, pucBuf, iLen);
        return STAT_ERR_INVAILD_PARAM;
    }

    if (gstUartCfg[enChan].iFd < 0)
    {
        LOG_ERROR("Chan %d is not open", enChan);
        return STAT_ERR_UART_NOT_OPEN;
    }

    ulRet = AppFile_Read(gstUartCfg[enChan].iFd, pucBuf, iLen);
    if (ulRet < 0)
    {
        LOG_ERROR("Read %s fail, err %s", gstUartCfg[enChan].szPath, strerror(errno));
        return STAT_ERR_FILE_READ;
    }

    return ulRet;
}

ULONG BspUart_Write(IN BSP_UART_CHANNEL_e enChan, INOUT UINT8 *pucBuf, IN SINT32 iLen)
{
    ULONG ulRet = STAT_SUCCESS;

    if (enChan >= BSP_UART_CHANNEL_MAX || pucBuf == NULL || iLen <= 0)
    {
        LOG_ERROR("Invalid channel %d or buf %p or len %d", enChan, pucBuf, iLen);
        return STAT_ERR_INVAILD_PARAM;
    }

    if (gstUartCfg[enChan].iFd < 0)
    {
        LOG_ERROR("Chan %d is not open", enChan);
        return STAT_ERR_UART_NOT_OPEN;
    }

    ulRet = AppFile_Write(gstUartCfg[enChan].iFd, pucBuf, iLen);
    if (ulRet < 0)
    {
        LOG_ERROR("Write %s fail, err %s", gstUartCfg[enChan].szPath, strerror(errno));
        return STAT_ERR_FILE_WRITE;
    }

    return ulRet;
}

/**********************************************************
 * Function Name:        BspUart_Init
 * Description:          初始化串口
 * Parameters[in]:       
 * Parameters[out]:      
 * ReturnValue:          ULONG
 * Author:               liuke
 * Date:                 2024.05.13
***********************************************************/
ULONG BspUart_Init(void)
{
    SINT32 i = 0;
    ULONG ulRet = STAT_SUCCESS;

    for (i = 0; i < BSP_UART_CHANNEL_MAX; i++)
    {
        if (gstUartCfg[i].isEnable == BOOL_TRUE)
        {
            ulRet = BspUart_Open(i, &gstUartCfg[i].iFd);
            if (ulRet != STAT_SUCCESS)
            {
                break;
            }
        }
    }

    if (ulRet != STAT_SUCCESS)
    {
        for (i = 0; i < BSP_UART_CHANNEL_MAX; i++)
        {
            if (gstUartCfg[i].isEnable == BOOL_TRUE && gstUartCfg[i].iFd >= 0)
            {
                close(gstUartCfg[i].iFd);
                gstUartCfg[i].iFd = -1;
            }
        }
    }

    return ulRet;
}
